package io.netty.myExample.duyutian.exercise.exerciseOne;


import javax.xml.bind.annotation.W3CDomHandler;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 测试案例一：
 * <p>
 * 测试当服务端发送给客户端
 * 客户端的接收缓冲区满了
 * 服务端监听写请求
 * 之后客户端开始消费了
 * 是否会触发写请求 (会)
 * 是否可以同时监听两种请求 (通过与运算可以监听多个感兴趣的事件)
 *
 * </p>
 *
 * <p>
 * 镜像案例:
 *
 *
 * </p>
 */
public class ExerciseOneService {
    /**
     * 选择器
     */
    private static Selector selector;

    public static void main(String[] args) throws IOException, InterruptedException {
        //创建通道管理器(Selector)
        selector = Selector.open();
        //创建通道ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //将通道设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //将ServerSocketChannel对应的ServerSocket绑定到指定端口(port)
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(8989));
        /**         * 将通道(Channel)注册到通道管理器(Selector)，并为该通道注册selectionKey.OP_ACCEPT事件         * 注册该事件后，当事件到达的时候，selector.select()会返回，         * 如果事件没有到达selector.select()会一直阻塞。         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //循环处理
        while (true) {
            //当注册事件到达时，方法返回，否则该方法会一直阻塞
            selector.select();
            //获取监听事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            //迭代处理
            while (iterator.hasNext()) {
                //获取事件
                SelectionKey key = iterator.next();
                //移除事件，避免重复处理
                iterator.remove();
                //检查是否是一个就绪的可以被接受的客户端请求连接
                if (key.isAcceptable()) {
                    handleAccept(key);
                } else if (key.isReadable()) {
                    //检查套接字是否已经准备好读数据
                    handleRead(key);
                } else if (key.isWritable()) {
                    //检查该套接字是否可以写
                    handleWrite(key);
                }
            }
        }
    }

    /**
     * 处理写事件
     *
     * @param key
     */
    private static void handleWrite(SelectionKey key) throws IOException {
        System.out.println("触发了写事件！");
        SocketChannel socketChannel = (SocketChannel) key.channel();
        socketChannel.write(ByteBuffer.wrap("server last msg".getBytes()));
    }

    /**
     * 处理客户端连接成功事件
     */
    private static void handleAccept(SelectionKey key) throws IOException {
        //获取客户端连接通道
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = server.accept();
        socketChannel.configureBlocking(false);
        //给通道设置读事件，客户端监听到读事件后，进行读取操作
        socketChannel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
        //socketChannel.register(selector, SelectionKey.OP_READ);
        //handleCycleWrite(socketChannel);
    }

    private static void handleCycleWrite(SocketChannel socketChannel) throws IOException {
        String msg = "Hello Client!";
        //继续总字节数
        int totalByte = 0;
        while (true) {
            byte[] bytes = msg.getBytes();
            ByteBuffer wrap = ByteBuffer.wrap(bytes);
            totalByte += bytes.length;
            int write = socketChannel.write(wrap);
            if (write == 0) {
                //当不可写入的时候监听写事件
                socketChannel.register(selector, SelectionKey.OP_WRITE);
                System.out.println("注册啦写事件! 总长度:" + totalByte);
                break;
            }
        }

    }


    /**
     * 监听到读事件，读取客户端发送过来的消息
     */
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        //从通道读取数据到缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(128);
        channel.read(buffer);
        //输出客户端发送过来的消息
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("触发了读事件:" + msg);
    }
}
